package com.zzh.partnersys.ai.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.zzh.partnersys.ai.entity.response.FileUploadResponse;
import com.zzh.partnersys.common.exception.ResultCodeEnum;
import com.zzh.partnersys.common.exception.ResultUtils;
import io.minio.*;
import io.minio.http.Method;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@Slf4j
@Tag(name = "Minio接口")
@RestController
@RequestMapping("/minio")
public class MinioController {
    @Resource
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Value("${minio.presigned-url-expiry:604800}")
    private int presignedUrlExpiry; // 预签名URL有效期（秒），默认7天
 
    /**
     * 单个文件上传
     * 
     * @param file 上传的文件
     * @return 文件上传响应，包含URL和文件名称
     */
    @Operation(summary = "单个文件上传")
    @PostMapping("/upload")
    public ResultUtils<FileUploadResponse> upload(@RequestParam("file") MultipartFile file) {
        // 参数校验
        if (file == null || file.isEmpty()) {
            return ResultUtils.fail(null);
        }

        String originalFileName = file.getOriginalFilename();
        // 生成文件名(日期文件夹)
        String suffix = FileUtil.getSuffix(originalFileName);
        String uuid = IdUtil.simpleUUID();
        String fileName = DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + "/" + uuid + "." + suffix;
        
        // try-with-resource 语法糖自动释放流
        try (InputStream inputStream = file.getInputStream()) {
            // 文件上传
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .contentType(file.getContentType())
                    .stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);

            // 生成预签名URL
            String fileUrl = generatePresignedUrl(fileName);
            
            // 返回响应对象
            FileUploadResponse response = new FileUploadResponse(fileUrl, fileName, originalFileName);
            return ResultUtils.ok(response);
        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            return ResultUtils.fail(null);
        }
    }

    /**
     * 批量文件上传
     * 支持上传多个文件，即使部分文件上传失败，也会返回成功上传的文件列表
     * 
     * @param files 上传的文件列表
     * @return 成功上传的文件列表，包含URL和文件名称
     */
    @Operation(summary = "批量文件上传")
    @PostMapping("/upload/batch")
    public ResultUtils<List<FileUploadResponse>> uploadBatch(@RequestParam("files") MultipartFile[] files) {
        // 参数校验
        if (files == null || files.length == 0) {
            return ResultUtils.fail(null);
        }

        List<FileUploadResponse> successList = new ArrayList<>();
        
        // 遍历所有文件，逐个上传
        for (MultipartFile file : files) {
            // 跳过空文件
            if (file == null || file.isEmpty()) {
                log.warn("跳过空文件: {}", file != null ? file.getOriginalFilename() : "null");
                continue;
            }

            String originalFileName = file.getOriginalFilename();
            // 生成文件名(日期文件夹)
            String suffix = FileUtil.getSuffix(originalFileName);
            String uuid = IdUtil.simpleUUID();
            String fileName = DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + "/" + uuid + "." + suffix;
            
            try (InputStream inputStream = file.getInputStream()) {
                // 文件上传
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .contentType(file.getContentType())
                        .stream(inputStream, inputStream.available(), -1)
                        .build();
                minioClient.putObject(putObjectArgs);

                // 生成预签名URL
                String fileUrl = generatePresignedUrl(fileName);
                
                // 添加到成功列表
                FileUploadResponse response = new FileUploadResponse(fileUrl, fileName, originalFileName);
                successList.add(response);
                
                log.info("文件上传成功: {}", originalFileName);
            } catch (Exception e) {
                // 单个文件上传失败，记录日志但不影响其他文件
                log.error("文件上传失败: {}, 错误: {}", originalFileName, e.getMessage(), e);
            }
        }

        // 返回成功上传的文件列表（即使部分失败，也返回成功的列表）
        return ResultUtils.ok(successList);
    }

    /**
     * 刷新/重新生成预签名URL（通过文件路径）
     * 根据文件路径重新生成预签名URL，无需判断原URL是否可访问
     * 
     * @param filePath 文件路径，例如：20251117/2f89caef063b41ae85f1bfb584bf63f3.pdf
     * @return 新的预签名URL
     */
    @GetMapping("/refresh-url")
    public String refreshPresignedUrl(@RequestParam("filePath") String filePath) {
        try {
            // 验证文件是否存在（可选，如果文件不存在会抛出异常）
            StatObjectArgs statObjectArgs = StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filePath)
                    .build();
            minioClient.statObject(statObjectArgs);
            
            // 重新生成预签名URL
            String newUrl = generatePresignedUrl(filePath);
            return newUrl;
        } catch (Exception e) {
            e.printStackTrace();
            return "刷新失败: " + e.getMessage();
        }
    }

    /**
     * 刷新/重新生成预签名URL（通过完整URL）
     * 从预签名URL中自动提取文件路径，然后重新生成新的预签名URL
     * 
     * @param presignedUrl 完整的预签名URL
     * @return 新的预签名URL
     */
    @GetMapping("/refresh-url-by-url")
    public String refreshPresignedUrlByUrl(@RequestParam("url") String presignedUrl) {
        try {
            // 从URL中提取文件路径
            String filePath = extractFilePathFromUrl(presignedUrl);
            if (filePath == null) {
                return "无法从URL中提取文件路径，请检查URL格式";
            }
            
            // 验证文件是否存在
            StatObjectArgs statObjectArgs = StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filePath)
                    .build();
            minioClient.statObject(statObjectArgs);
            
            // 重新生成预签名URL
            String newUrl = generatePresignedUrl(filePath);
            return newUrl;
        } catch (Exception e) {
            e.printStackTrace();
            return "刷新失败: " + e.getMessage();
        }
    }

    /**
     * 根据URL删除文件
     * 从预签名URL或完整URL中自动提取文件路径，然后删除文件
     * 
     * @param url 文件的URL（可以是预签名URL或完整URL）
     * @return 删除结果
     */
    @Operation(summary = "根据URL删除文件")
    @DeleteMapping("/delete-by-url")
    public ResultUtils<Boolean> deleteByUrl(@RequestParam("url") String url) {
        try {
            // 参数校验
            if (StringUtils.isBlank(url)) {
                return ResultUtils.build(false, ResultCodeEnum.PARAMS_ERROR);
            }

            // 从URL中提取文件路径
            String filePath = extractFilePathFromUrl(url);
            if (filePath == null) {
                return ResultUtils.build(false, ResultCodeEnum.PARAMS_ERROR);
            }

            // 删除文件
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filePath)
                    .build();
            minioClient.removeObject(removeObjectArgs);

            return ResultUtils.ok(true);
        } catch (Exception e) {
            return ResultUtils.build(false, ResultCodeEnum.SYSTEM_ERROR);
        }
    }

    /**
     * 根据文件路径删除文件
     * 
     * @param filePath 文件路径，例如：20251117/2f89caef063b41ae85f1bfb584bf63f3.pdf
     * @return 删除结果
     */
    @Operation(summary = "根据文件路径删除文件")
    @DeleteMapping("/delete")
    public ResultUtils<Boolean> deleteByPath(@RequestParam("filePath") String filePath) {
        try {
            // 参数校验
            if (StringUtils.isBlank(filePath)) {
                return ResultUtils.build(false, ResultCodeEnum.PARAMS_ERROR);
            }

            // 删除文件
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filePath)
                    .build();
            minioClient.removeObject(removeObjectArgs);

            return ResultUtils.ok(true);
        } catch (Exception e) {
            return ResultUtils.build(false, ResultCodeEnum.SYSTEM_ERROR);
        }
    }

    /**
     * 检查预签名URL是否过期
     * 
     * @param presignedUrl 预签名URL
     * @return JSON格式的检查结果，包含是否过期、过期时间等信息
     */
    @GetMapping("/check-url-expiry")
    public ResultUtils<Map<String, Object>> checkUrlExpiry(@RequestParam("url") String presignedUrl) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 解析URL参数
            Map<String, String> urlParams = parseUrlParams(presignedUrl);
            String amzDate = urlParams.get("X-Amz-Date");
            String amzExpires = urlParams.get("X-Amz-Expires");
            
            if (amzDate == null || amzExpires == null) {
                result.put("valid", false);
                result.put("message", "URL格式不正确，缺少必要的签名参数");
                return ResultUtils.fail(result);
            }
            
            // 解析日期：格式为 20251117T073606Z
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
            ZonedDateTime urlCreateTime = ZonedDateTime.parse(amzDate, formatter.withZone(ZoneId.of("UTC")));
            
            // 计算过期时间
            long expiresSeconds = Long.parseLong(amzExpires);
            ZonedDateTime expiryTime = urlCreateTime.plusSeconds(expiresSeconds);
            ZonedDateTime now = ZonedDateTime.now(ZoneId.of("UTC"));
            
            // 判断是否过期
            boolean isExpired = now.isAfter(expiryTime);
            long remainingSeconds = isExpired ? 0 : expiryTime.toEpochSecond() - now.toEpochSecond();
            
            result.put("valid", true);
            result.put("expired", isExpired);
            result.put("createTime", urlCreateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
            result.put("expiryTime", expiryTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
            result.put("remainingSeconds", remainingSeconds);
            result.put("remainingDays", remainingSeconds / 86400.0);
            result.put("message", isExpired ? "URL已过期" : "URL未过期，剩余 " + (remainingSeconds / 86400) + " 天");
            
        } catch (Exception e) {
            result.put("valid", false);
            result.put("message", "解析URL失败: " + e.getMessage());
            return ResultUtils.fail(result);
        }
        return ResultUtils.ok(result);
    }

    /**
     * 生成预签名URL的通用方法
     * 
     * @param fileName 文件路径
     * @return 预签名URL
     */
    private String generatePresignedUrl(String fileName) {
        try {
            GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .method(Method.GET);
            
            // 如果配置了有效期且大于0，则设置有效期；否则使用默认值（通常为7天）
            if (presignedUrlExpiry > 0) {
                builder.expiry(presignedUrlExpiry);
            }
            
            return minioClient.getPresignedObjectUrl(builder.build());
        } catch (Exception e) {
            throw new RuntimeException("生成预签名URL失败: " + e.getMessage(), e);
        }
    }

    /**
     * 从预签名URL中提取文件路径
     * 例如：http://127.0.0.1:9000/partnersysai/20251117/xxx.pdf?... 
     * 提取为：20251117/xxx.pdf
     * 
     * @param presignedUrl 预签名URL
     * @return 文件路径
     */
    private String extractFilePathFromUrl(String presignedUrl) {
        try {
            // 移除查询参数
            String urlWithoutParams = presignedUrl;
            if (presignedUrl.contains("?")) {
                urlWithoutParams = presignedUrl.substring(0, presignedUrl.indexOf("?"));
            }
            
            // 提取路径部分：/bucketName/filePath
            // 例如：http://127.0.0.1:9000/partnersysai/20251117/xxx.pdf
            int bucketIndex = urlWithoutParams.indexOf("/" + bucketName + "/");
            if (bucketIndex == -1) {
                return null;
            }
            
            // 提取文件路径（去掉bucket名称）
            String filePath = urlWithoutParams.substring(bucketIndex + bucketName.length() + 2);
            
            // URL解码
            filePath = URLDecoder.decode(filePath, StandardCharsets.UTF_8);
            
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解析URL中的查询参数
     * 
     * @param url 完整URL
     * @return 参数Map
     */
    private Map<String, String> parseUrlParams(String url) {
        Map<String, String> params = new HashMap<>();
        try {
            if (!url.contains("?")) {
                return params;
            }
            
            String queryString = url.substring(url.indexOf("?") + 1);
            String[] pairs = queryString.split("&");
            
            for (String pair : pairs) {
                int idx = pair.indexOf("=");
                if (idx > 0) {
                    String key = URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8);
                    String value = URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8);
                    params.put(key, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return params;
    }

}